package com.wfbi.security.chapter31.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.ehcache.EhCacheFactoryBean;
import org.springframework.cache.ehcache.EhCacheManagerFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.access.PermissionEvaluator;
import org.springframework.security.acls.AclPermissionEvaluator;
import org.springframework.security.acls.domain.*;
import org.springframework.security.acls.jdbc.BasicLookupStrategy;
import org.springframework.security.acls.jdbc.JdbcMutableAclService;
import org.springframework.security.acls.jdbc.LookupStrategy;
import org.springframework.security.acls.model.AclCache;
import org.springframework.security.acls.model.PermissionGrantingStrategy;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.core.authority.SimpleGrantedAuthority;

import javax.sql.DataSource;

/**
 * @author wfbi
 * @version 1.0
 * @date 2022/1/10 4:41 PM
 */
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class AclConfig {

    @Autowired
    DataSource dataSource;

    /**
     * AclAuthorizationStrategy 实例用来判断当前的认证主体是否有修改 Acl 的权限，准确来说是三种权限：修改 Acl 的 owner；修改 Acl 的审计信息以及修改 ACE 本身。这个接口只有一个实现类就是 AclAuthorizationStrategyImpl，我们在创建实例时，可以传入三个参数，分别对应了这三种权限，也可以传入一个参数，表示这一个角色可以干三件事。
     *
     * @return
     */
    @Bean
    public AclAuthorizationStrategy aclAuthorizationStrategy() {
        return new AclAuthorizationStrategyImpl(new SimpleGrantedAuthority("ROLE_ADMIN"));
    }

    /**
     * PermissionGrantingStrategy 接口提供了一个 isGranted 方法，这个方法就是最终真正进行权限比对的方法，该接口只有一个实现类 DefaultPermissionGrantingStrategy，直接 new 就行了。
     *
     * @return
     */
    @Bean
    public PermissionGrantingStrategy permissionGrantingStrategy() {
        return new DefaultPermissionGrantingStrategy(new ConsoleAuditLogger());
    }

    /**
     * 在 ACL 体系中，由于权限比对总是要查询数据库，造成了性能问题，因此引入了 Ehcache 做缓存。AclCache 共有两个实现类：SpringCacheBasedAclCache 和 EhCacheBasedAclCache。我们前面已经引入了 ehcache 实例，所以这里配置 EhCacheBasedAclCache 实例即可
     *
     * @return
     */
    @Bean
    public AclCache aclCache() {
        return new EhCacheBasedAclCache(aclEhCacheFactoryBean().getObject(), permissionGrantingStrategy(), aclAuthorizationStrategy());
    }

    @Bean
    public EhCacheFactoryBean aclEhCacheFactoryBean() {
        EhCacheFactoryBean ehCacheFactoryBean = new EhCacheFactoryBean();
        ehCacheFactoryBean.setCacheManager(aclCacheManager().getObject());
        ehCacheFactoryBean.setCacheName("aclCache");
        return ehCacheFactoryBean;
    }

    @Bean
    public EhCacheManagerFactoryBean aclCacheManager() {
        return new EhCacheManagerFactoryBean();
    }

    /**
     * LookupStrategy 可以通过 ObjectIdentity 解析出对应的 Acl。LookupStrategy 只有一个实现类就是 BasicLookupStrategy，直接 new 即可。
     *
     * @return
     */
    @Bean
    public LookupStrategy lookupStrategy() {
        return new BasicLookupStrategy(dataSource, aclCache(), aclAuthorizationStrategy(), new ConsoleAuditLogger()
        );
    }

    @Bean
    public JdbcMutableAclService aclService() {
        JdbcMutableAclService jdbcMutableAclService = new JdbcMutableAclService(dataSource, lookupStrategy(), aclCache());
        jdbcMutableAclService.setClassIdentityQuery("SELECT @@IDENTITY");
        jdbcMutableAclService.setSidIdentityQuery("SELECT @@IDENTITY");
        return jdbcMutableAclService;
    }

    /**
     * PermissionEvaluator 是为表达式 hasPermission 提供支持的。由于本案例后面使用类似于 @PreAuthorize("hasPermission(#noticeMessage, 'WRITE')") 这样的注解进行权限控制，因此之类需要配置一个 PermissionEvaluator 实例
     *
     * @return
     */
    @Bean
    PermissionEvaluator permissionEvaluator() {
        AclPermissionEvaluator permissionEvaluator = new AclPermissionEvaluator(aclService());
        return permissionEvaluator;
    }
}
